package distributedbank.client;

import distributedbank.ResponseThread;
import distributedbank.Wrapper;
import distributedbank.requests.*;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.*;
import javax.swing.text.*;
import java.text.*;

/**
 * ClientUI is a mock ATM. Its JvmId equals to the corresponding bank branch's
 * JvmId + 4.
 */
public class ClientUI implements ActionListener
{
	/**
	 * Construct a ClientUI
	 * @param JvmNumber
	 * 		5 - 8
	 */
    public ClientUI( int JvmNumber ) {
        branchNumber = JvmNumber - 4;
        initComponents();
    }

    /**
     * Window Size
     */
    private static final int width = 500;

    /**
     * Window Size
     */
    private static final int height = 600;

    /**
     * branchNumber = 1-4, not 5-8
     */
    private int branchNumber;

    /**
     * Each ClientUI generates distinct serialNumbers. This one is the number of
     * serialNumbers this ClientUI generated.
     */
    private static int serialCount = 0;

    private MaskFormatter numberOf2;

    private MaskFormatter numberOf5;

    private JFormattedTextField accountNumberArea;

    private JFormattedTextField transferBranchArea;

    private JFormattedTextField transferAccountArea;

    private JTextField amountIntArea;

    private JFormattedTextField amountFloatArea;

    private JFrame frame;

    private JFrame ssFrame;
    
    private JTextArea ssArea;
    
    private JScrollPane ssAreaScr;
    
    private JTextArea hintArea;

    private JScrollPane hintAreaScr;

    private JPanel framePanel;

    private JPanel leftPanel;

    private JPanel rightPanel;

    private JTextArea statusArea;

    private JScrollPane statusAreaScr;

    private JPanel lowerPanel;

    private JPanel accountPanel;

    private JPanel transferPanel;

    private JPanel amountPanel;

    private JLabel accountNumberLabel;

    private JLabel branchNumberLabel;

    private JLabel transferToLabel;

    private JLabel amountLabel;

    private JLabel dotLabel;

    private JButton depositButton;

    private JButton withdrawButton;

    private JButton queryButton;

    private JButton transferButton;

    private JButton snapShotButton;

    private final static String hint = "NOW you can take snapshots..So good.";
    
    private void initComponents() {
        try {
            numberOf2 = new MaskFormatter( "##" );
            numberOf5 = new MaskFormatter( "#####" );
        } catch( ParseException pe ) {
        }
        accountNumberArea = new JFormattedTextField( numberOf5 );
        transferBranchArea = new JFormattedTextField( numberOf2 );
        transferAccountArea = new JFormattedTextField( numberOf5 );
        amountIntArea = new JTextField( 5 );
        amountFloatArea = new JFormattedTextField( numberOf2 );
        ssFrame = new JFrame( "Snapshot Information at Branch "  + branchNumber );
        frame = new JFrame( "Branch " + branchNumber );
        hintArea = new JTextArea( hint, 5, 1 );
        hintAreaScr = new JScrollPane( hintArea );
        hintArea.setEditable( false );
        leftPanel = new JPanel();
        rightPanel = new JPanel();
        statusArea = new JTextArea( 7, 1 );
        statusAreaScr = new JScrollPane( statusArea );
        statusArea.setEditable( false );
        lowerPanel = new JPanel();
        framePanel = new JPanel();
        accountPanel = new JPanel();
        transferPanel = new JPanel();
        amountPanel = new JPanel();
        ssArea = new JTextArea();
        ssAreaScr = new JScrollPane(ssArea);
        accountNumberLabel = new JLabel( "Account #" );
        accountNumberLabel.setHorizontalAlignment( JLabel.CENTER );
        branchNumberLabel = new JLabel( "0" + Integer.toString( branchNumber ) );
        branchNumberLabel.setHorizontalAlignment( JLabel.CENTER );
        transferToLabel = new JLabel( "Transfer To" );
        transferToLabel.setHorizontalAlignment( JLabel.CENTER );
        amountLabel = new JLabel( "# of amount" );
        amountLabel.setHorizontalAlignment( JLabel.CENTER );
        dotLabel = new JLabel( "." );
        dotLabel.setHorizontalAlignment( JLabel.CENTER );
        depositButton = new JButton( "Deposit" );
        withdrawButton = new JButton( "Withdraw" );
        queryButton = new JButton( "Query" );
        transferButton = new JButton( "Transfer" );
        snapShotButton = new JButton( "Snapshot" );
        snapShotButton.addActionListener( this );
        depositButton.addActionListener( this );
        withdrawButton.addActionListener( this );
        queryButton.addActionListener( this );
        transferButton.addActionListener( this );
        amountPanel.setLayout( new GridLayout( 1, 3 ) );
        amountIntArea.setHorizontalAlignment( JLabel.CENTER );
        amountPanel.add( amountIntArea );
        amountPanel.add( dotLabel );
        amountFloatArea.setText( "00" );
        amountFloatArea.setHorizontalAlignment( JLabel.CENTER );
        amountPanel.add( amountFloatArea );
        frame.setLayout( new BorderLayout() );
        frame.add( "South", hintAreaScr );
        frame.add( framePanel );
        framePanel.setLayout( new GridLayout( 1, 2 ) );
        framePanel.add( leftPanel );
        framePanel.add( rightPanel );
        leftPanel.setLayout( new GridLayout( 6, 1 ) );
        leftPanel.add( accountNumberLabel );
        leftPanel.add( accountPanel );
        leftPanel.add( transferToLabel );
        leftPanel.add( transferPanel );
        leftPanel.add( amountLabel );
        leftPanel.add( amountPanel );
        accountPanel.setLayout( new GridLayout( 1, 2 ) );
        accountPanel.add( branchNumberLabel );
        accountNumberArea.setHorizontalAlignment( JLabel.CENTER );
        accountPanel.add( accountNumberArea );
        transferPanel.setLayout( new GridLayout( 1, 2 ) );
        transferBranchArea.setHorizontalAlignment( JLabel.CENTER );
        transferPanel.add( transferBranchArea );
        transferAccountArea.setHorizontalAlignment( JLabel.CENTER );
        transferPanel.add( transferAccountArea );
        rightPanel.setLayout( new GridLayout( 2, 1 ) );
        rightPanel.add( statusAreaScr );
        rightPanel.add( lowerPanel );
        lowerPanel.setLayout( new GridLayout( 5, 1 ) );
        lowerPanel.add( depositButton );
        lowerPanel.add( withdrawButton );
        lowerPanel.add( queryButton );
        lowerPanel.add( transferButton );
        lowerPanel.add( snapShotButton );
        frame.setSize( width, height );
        frame.setVisible( true );
        frame.setResizable( false );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        ssFrame.setSize( width, height );
        ssFrame.setVisible( false );
        ssFrame.setResizable( false );
        ssFrame.add(ssAreaScr);
        
        try {
            ServerSocket serverSocket = new ServerSocket( Wrapper.basePort
                    + branchNumber + 4 );
            while( true ) {
                Socket clientSocket = serverSocket.accept();
                new ResponseThread( this, clientSocket ).start();
            }
        } catch( IOException e ) {
            System.out.println( e );
        }
    }

    /**
     * Use the branchNumber as the last digit of the serialNumber With a
     * serialCount, there won't be same serialNumbers.
     * 
     * @return 
     * 		serial number
     */
    private int generateSerial() {
        // generate serial number
        // jvm 5-8
        int serial = branchNumber + serialCount;
        serialCount += 10;
        return serial;
    }

    /**
     * Append information about Snapshots
     * @param s
     * 		new snapshot information
     */
    public void addInfoToSs(String s){
    	ssArea.setText(ssArea.getText() + s);
    }
    
    /**
     * Make the snapshot information frame appears.
     */
    public void setSsFrameVisible(){
    	if(!ssFrame.isVisible()){
    		ssFrame.setVisible(true);
    	}
    }
    /**
     * Read response message and show them on statusArea
     */
    public void readMessage( String str ) {
        this.statusArea.append( str + "\n" );
    }

    /**
     * ActionEvents happens when buttons are pressed.
     */
    public void actionPerformed( ActionEvent e ) {
        String s = e.getActionCommand();
        int serialNumber = generateSerial();
        /**
         * For Deposit\Withdraw\Query, srcAcctNumber means accountNumber
         */
        int srcAcctNumber;
        int amount;
        int dstAcctNumber;
        int dstId;
        
        if( "Snapshot".equals( s ) ) {
            Wrapper wrapper = new Wrapper();
            Serializable message = new SsRequest( 0, branchNumber + 4 );
            if( !wrapper.sendMessage( message, branchNumber + 4, branchNumber ) )
                readMessage( "Cannot connect to local bank branch! \nCannot process!" );
        } else if( "Deposit".equals( s ) ) {
        	try {
                srcAcctNumber = Integer.parseInt( accountNumberArea.getText() );
            } catch( java.lang.NumberFormatException nfe ) {
                statusArea.append( "Check your input account number and retry.\n" );
                return;
            }
            try {
                amount = Integer.parseInt( amountFloatArea.getText() ) + 100
                        * Integer.parseInt( amountIntArea.getText() );
            } catch( java.lang.NumberFormatException nfe ) {
                statusArea
                        .append( "Check your input amount \n(especially the 2-digit "
                                + "\nfloating number)and retry.\n" );
                return;
            }
            // send message
            Wrapper wrapper = new Wrapper();
            Serializable message = new DepositRequest( branchNumber,
                    srcAcctNumber, serialNumber, branchNumber + 4, amount );
            if( !wrapper.sendMessage( message, branchNumber + 4, branchNumber ) )
                readMessage( "Cannot connect to local bank branch! \nCannot process!" );
        } else if( "Withdraw".equals( s ) ) {
        	try {
                srcAcctNumber = Integer.parseInt( accountNumberArea.getText() );
            } catch( java.lang.NumberFormatException nfe ) {
                statusArea.append( "Check your input account number and retry.\n" );
                return;
            }
            try {
                amount = Integer.parseInt( amountFloatArea.getText() ) + 100
                        * Integer.parseInt( amountIntArea.getText() );
            } catch( java.lang.NumberFormatException nfe ) {
                statusArea
                        .append( "Check your input amount \n(especially the 2-digit "
                                + "floating number)\nand retry.\n" );
                return;
            }
            // send deposit request, deposit a negative amount of money.
            Wrapper wrapper = new Wrapper();
            Serializable message = new DepositRequest( branchNumber,
                    srcAcctNumber, serialNumber, branchNumber + 4, -amount );
            if( !wrapper.sendMessage( message, branchNumber + 4, branchNumber ) )
                readMessage( "Cannot connect to local bank branch! \nCannot process!" );
        } else if( "Query".equals( s ) ) {
        	try {
                srcAcctNumber = Integer.parseInt( accountNumberArea.getText() );
            } catch( java.lang.NumberFormatException nfe ) {
                statusArea.append( "Check your input account number and retry.\n" );
                return;
            }
            Wrapper wrapper = new Wrapper();
            Serializable message = new QueryRequest( branchNumber,
                    srcAcctNumber, serialNumber, branchNumber + 4 );
            if( !wrapper.sendMessage( message, branchNumber + 4, branchNumber ) )
                readMessage( "Cannot connect to local bank branch! \nCannot process!" );
        } else if( "Transfer".equals( s ) ) {
        	try {
                srcAcctNumber = Integer.parseInt( accountNumberArea.getText() );
            } catch( java.lang.NumberFormatException nfe ) {
                statusArea.append( "Check your input account number and retry.\n" );
                return;
            }
            try {
                amount = Integer.parseInt( amountFloatArea.getText() ) + 100
                        * Integer.parseInt( amountIntArea.getText() );
                dstAcctNumber = Integer
                        .parseInt( transferAccountArea.getText() );
                dstId = Integer.parseInt( transferBranchArea.getText() );
            } catch( java.lang.NumberFormatException nfe ) {
                statusArea.append( "Check your input account and\n"
                        + "amount (especially the 2-digit \n"
                        + "floating number)and retry.\n" );
                return;
            }
            Wrapper wrapper = new Wrapper();
            Serializable message = new TransferRequest( branchNumber,
                    srcAcctNumber, serialNumber, branchNumber + 4, dstId,
                    dstAcctNumber, amount );
            if( !wrapper.sendMessage( message, branchNumber + 4, branchNumber ) )
                readMessage( "Cannot connect to local bank branch! \nCannot process!" );
        }
    }
}